/*
 *  mmap.c
 *  Prex
 *
 *  Created by Travis Mayberry on 3/14/11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include <mmap.h>
#include <stdlib.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/prex.h>
#include <signal.h>
#include <sys/posix.h>
#include <ipc/fs.h>

#include <stddef.h>
#include <errno.h>

int nummaps = 0;
static int registered = 0;

void mmap_handler(int code)
{
	printf("At handler, code %d\n", code);
	if( code == SIGUSR1 )
	{
		int x;
		int found = 0;
		int addr = getcr2();
		int ecode = geteflags();
		void * addrp = &addr;
		int toread;
		vaddr_t start, end;
		struct mmap_t * cur = 0;
		
		printf("Got address %d\n", addr);
		
		if( ecode & 1 )
		{
			printf("Protection fault\n");
			exit(-1);
		}
		
		for( x = 0; x < nummaps && !found; x++ )
		{
			cur = &maps[x];
			if( cur -> vaddr <= addr && cur -> vaddr + cur -> size >= addr )
			{
				found = 1;
			}
		}
		
		/* Find start and end of page */
		start = trunc_page((vaddr_t)addr);
		end = round_page((vaddr_t)addr+1);
		
		/* If start is less than the beginning of the map, change it */
		if( start < cur->vaddr )
		{
			start = cur->vaddr;
		}
		
		/* If there is more than one page in the map, just read the first */
		toread = end - start;
		if( start + toread > cur->vaddr + cur->size )
		{
			toread = cur->size - (start - cur->vaddr);
		}
		
		if( toread > PAGE_SIZE ) toread = PAGE_SIZE;
		
		/* If not found in any map, exit */
		if( !found )
		{
			printf("Fatal page fault at %d\n", addr);
			exit(-1);
		}
		
		printf("New addr %d trunc start %d page size %d\n", addr, (int)start, PAGE_SIZE);
		
		addrp = (void*)start;
		if( vm_allocate(task_self(), &addrp, 1, 0) )
		{
			printf("Error allocating memory\n");
			exit(-1);
		}
		
		/* Seek to correct location in file */
		printf("Seeking %ld\n", start - cur->vaddr);
		lseek(cur->fd, start - cur->vaddr + cur->offset, SEEK_SET);
		read(cur->fd, start, toread);
		printf("Done handler\n");
		   
		if( vm_attribute(task_self(), start, PROT_READ) )
			printf("Error setting page attribute\n");
		
	}
	else
	{
		exit(-1);
	}
	exception_return();
}

void mmap(void * raddr, int fd, int offset, int size)
{
	void * vaddr = raddr;
	
	if( !registered )
	{
		exception_setup(mmap_handler);
		registered = 1;
	}
	
	maps[nummaps].vaddr = (int)vaddr;
	maps[nummaps].size = size;
	maps[nummaps].offset = offset;
	maps[nummaps].fd = fd;
	
	nummaps++;
	
	printf("Done mmap\n");
}